package edu.arizona.sista.learn.activity.model;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import edu.arizona.cs.learn.timeseries.model.Instance;
import edu.arizona.cs.learn.timeseries.model.Interval;
import edu.arizona.sista.learn.activity.util.Utils;


public class ActivityInstance {
	
	public final static int UNDEFINED = -1;
	
	private Map<String, List<PredicateInterval>> _intervalsMap;
	private int _id;
	private String _filename;
	private int _start = 0;		// assume to start at time 0
	private int _end;
	
	public ActivityInstance() {
		this(UNDEFINED, null, new ArrayList<PredicateInterval>());
	}
	
	public ActivityInstance(int id) {
		this(id, null, new ArrayList<PredicateInterval>());
	}
	
	public ActivityInstance(List<PredicateInterval> fluents) {
		this(UNDEFINED, null, fluents);
	}
	
	public ActivityInstance(int id, List<PredicateInterval> fluents) {
		this(id, null, fluents);
	}
	
	public ActivityInstance(String filename, List<PredicateInterval> fluents) {
		this(UNDEFINED, filename, fluents);
	}
	
	public ActivityInstance(int id, String filename, List<PredicateInterval> fluents) {
		_id = id;
		_filename = filename;
		_intervalsMap = new HashMap<String, List<PredicateInterval>>();
		for (PredicateInterval pi : fluents) {
			if (! _intervalsMap.containsKey(pi.getPredicate().getName())) {
				_intervalsMap.put(pi.getPredicate().getName(), new ArrayList<PredicateInterval>());
			}
			_intervalsMap.get(pi.getPredicate().getName()).add(pi);
		}
		
	}

	public ActivityInstance(Instance inst) {
		this(inst, null);
	}

	public ActivityInstance(Instance inst, String filename) {
		_id = inst.id();
		_filename = filename;
		_intervalsMap = new HashMap<String, List<PredicateInterval>>();
		for (Interval i : inst.intervals()) {
			PredicateInterval pi = new PredicateInterval(i);
			if (! _intervalsMap.containsKey(pi.getPredicate().getName())) {
				_intervalsMap.put(pi.getPredicate().getName(), new ArrayList<PredicateInterval>());
			}
			_intervalsMap.get(pi.getPredicate().getName()).add(pi);
		}
	}
	
	public ActivityInstance(Map<String, List<PredicateInterval>> intervalsMap, int id, String filename) {
		_intervalsMap = intervalsMap;
		_id = id;
		_filename = filename;
	}

	public void addInterval(PredicateInterval i) {
		
		if( i != null ) {
			
			PredicateInterval copy = i.copy();
			
			if (! _intervalsMap.containsKey(i.getPredicate().getName())) {
				_intervalsMap.put(i.getPredicate().getName(), new ArrayList<PredicateInterval>());
			} 
			else {
				
				List<PredicateInterval> intervals = _intervalsMap.get(copy.getPredicate().getName());
				List<Integer> toRemove = new ArrayList<Integer>();
				// Need to iterate backward to add 'removal' index in decreasing order.
				for( int j=intervals.size()-1; j>=0; j-- ){
					PredicateInterval interval = intervals.get(j);
					//if a matching interval meets or overlaps with the new one, we'll remove it and merge the two
					if( interval.getName().equalsIgnoreCase(copy.getName()) && 
							interval.start <= copy.end && interval.end >= copy.start ){
						toRemove.add(j);
						copy.start = Math.min(interval.start, copy.start);
						copy.end = Math.max(interval.end, copy.end);
					}
				}
				
				// Since we're removing items by indices, then we must remove in
				// decreasing order, or else the indices will be incorrectly offset
				// for subsequent removals.
				for( Integer j : toRemove )
					intervals.remove(j.intValue());
			}
			_intervalsMap.get(i.getPredicate().getName()).add(copy);
			if (i.end > _end) _end = i.end;		// extend activity's ending time
		}
	}
	
	public void addIntervals(List<PredicateInterval> is) {
		if( is != null )
			for( PredicateInterval i : is )
				addInterval(i);
	}
	
	public List<PredicateInterval> getIntervals(String name) {
		return _intervalsMap.get(name);
	}
	
	public Map<String, List<PredicateInterval>> getIntervalsMap() {
		return _intervalsMap;
	}
	
	public int id() {
		return _id;
	}
	
	public String filename() {
		return _filename;
	}
	
	public void setFilename(String filename) {
		_filename = filename;
	}
	
	public void setEnd(int e) {
		_end = e;
	}
	
	public int end() {
		return _end;
	}
	
	public int start() {
		return _start;
	}
	
	public void setStart(int s) {
		_start = s;
	}
	
	public ActivityInstance copy() {
		return new ActivityInstance(new HashMap<String, List<PredicateInterval>>(_intervalsMap), _id, _filename);
	}
	
	public static List<ActivityInstance> load(File file) {
		List<ActivityInstance> instances = new ArrayList<ActivityInstance>();
		for (Instance i : Instance.load(file)) {
			instances.add(new ActivityInstance(i, file.getName()));
		}
		return instances;
	}
	
	public void smoothFragmentedFluents() {
		double threshold = 10;
		_intervalsMap = Utils.glue(_intervalsMap, threshold, 2);
//		String[] preds = new String[]{ PredicateNames.MOVING, PredicateNames.MOVING_FAST_HUMAN };
//		for (String pred : preds) {
//			if (_intervalsMap.containsKey(pred)) {
//				List<PredicateInterval> smoothed = Utils.glue(_intervalsMap.get(pred), threshold, 2);
//				_intervalsMap.put(pred, smoothed);
//			}
//		}
	}
	
	public String toString() {
		return this.toStringBuffer().toString();
	}
	
	public StringBuffer toStringBuffer() {
		StringBuffer inst = new StringBuffer();
		inst.append("(" + _id + " (" + "\n");
		for (String key : _intervalsMap.keySet()) {
			List<PredicateInterval> intervals = _intervalsMap.get(key);
			for (PredicateInterval i : intervals) {
				inst.append("\t" + i.toString() + "\n");
			}
		}
		inst.append("))" + "\n");
		
		return inst;
	}
	
	public boolean toFile(File file, boolean append) {
		try {
			FileWriter fstream = new FileWriter(file, append);
			BufferedWriter out = new BufferedWriter(fstream);
			out.write(this.toString());
			out.close();
		} catch (Exception e) {
			System.err.println("Error: " + e.getMessage());
			return false;
		}

		return true;
	}

}
